Example pair
D_T8_C1R2_1 D_T8_C1R2_2 D_T8_C1R2_5-95_1_2
list_images <- tibble(
image_name = c("D_T8_C1R2_1", "D_T8_C1R2_2", "D_T8_C1R2_5-95_1_2"),
folder_original = rep(paste0(folder_main, "check/D-00-original/"), 3),
folder_green = rep(paste0(folder_main, "example/"), 3),
folder_rolled = rep(paste0(folder_main, "example/"), 3),
folder_watershed = rep(paste0(folder_main, "example/"), 3),
folder_red = rep(paste0(folder_main, "example/"), 3),
folder_blue = rep(paste0(folder_main, "example/"), 3)
)
i = 3
image_name <- list_images$image_name[i]
folder_original <- list_images$folder_original[i]
folder_green <- list_images$folder_green[i]
folder_red <- list_images$folder_red[i]
folder_blue <- list_images$folder_blue[i]
folder_rolled <- list_images$folder_rolled[i]
folder_watershed <- list_images$folder_rolled[i]
0. original image
image_original <- readImage(paste0(folder_original, image_name, ".tiff"))
writeImage(image_original, paste0(folder_green, image_name, "-00-original.tiff"))
display(image_original, method = "raster")

1. Green channel
temp <- image_original
colorMode(temp) = Grayscale
image_green <- temp[,,2]
writeImage(image_green, paste0(folder_green, image_name, "-01-green.tiff"))
display(image_green, method = "raster")

Red channel
temp <- image_original
colorMode(temp) = Grayscale
image_red <- temp[,,1]
writeImage(image_red, paste0(folder_red, image_name, "-11-red.tiff"))
display(image_red, method = "raster")

Blue channel
temp <- image_original
colorMode(temp) = Grayscale
image_blue <- temp[,,1]
writeImage(image_blue, paste0(folder_blue, image_name, "-21-blue.tiff"))
display(image_blue, method = "raster")

2. Rolling ball
Python code from rolling_ball.py
py$file_green <- paste0(folder_green, image_name, ".tiff")
py$file_rolled <- paste0(folder_rolled, image_name, "-02-rolled.tiff")
The R variables should be passed to python but somehow failed. Not
sure where the problem is from, probably reticualte. 20220902 use the
externally generated rolled images.
reticulate::repl_python()
import imageio
import os
import sys
import skimage
from skimage import data, restoration, util, io, color
def rolling_ball_light(image):
# 2. invert the image
image_inverted = util.invert(image)
# 3. rolling ball
background_inverted = restoration.rolling_ball(image_inverted, radius = 80, num_threads = 10)
# 4. invert the result
image_rolled_inverted = image_inverted - background_inverted
image_rolled = util.invert(image_rolled_inverted)
return image_rolled
image = io.imread(file_green)
NameError: name 'file_green' is not defined
image_rolled = rolling_ball_light(image)
NameError: name 'image' is not defined
io.imsave(file_rolled, image_rolled)
NameError: name 'file_rolled' is not defined
Display the rolled result
quit
image_rolled <- readImage(paste0(folder_rolled, image_name, "-02-rolled.tiff"))
display(image_rolled, method = "raster")

3. Thresholding
Here because the images have undergone gray scale and background
subtraction so I apply a global threshold to the image
threshold <- otsu(image_rolled)
image_thresholded <- image_rolled < threshold
display(image_thresholded, method = "raster")

4. Detect round shaped object and remove super small size
To select potential colonies: area, perimeter, and circularity.
This step is implemented here to prevent over segmentation and reduce
segmentation load
As you can see here, attached objects are not divided yet
image_object <- bwlabel(image_thresholded)
object_shape <- computeFeatures.shape(image_object) %>% as_tibble(rownames = "ObjectID")
object_shape_round <- object_shape %>%
# Area
filter(s.area > 500 & s.area < 500000) %>%
# Roundness = 1 means a perfect circle
mutate(Roundness = (s.radius.max - s.radius.min)/2) %>%
filter(Roundness > 0.1 & Roundness < 50) %>%
# Circularity = 1 means a perfect circle and goes down to 0 for non-cicular shapes
mutate(Circularity = 4 * pi * s.area / s.perimeter^2) %>%
filter(Circularity > 0.3) %>%
arrange(desc(s.area))
object_ID_nonround <- which(!(object_shape$ObjectID %in% object_shape_round$ObjectID))
image_round <- rmObjects(image_object, object_ID_nonround, reenumerate = F)
display(colorLabels(image_round), method = "raster")

5. Distance map
The distance map contains for each pixel the distance to the nearest
background pixe
image_distancemap <- distmap(image_round)
display(normalize(image_distancemap), method = "raster")

6. Watershed
This is the actual segmentation step. Here the adjacent objects are
better distinguished
image_watershed <- watershed(image_distancemap, tolerance = 1)
display(colorLabels(image_watershed), method = "raster")

Output the example
# writeImage(image_original, paste0(folder_main, "example/", image_name, "-00-original.tiff"))
# writeImage(image_green, paste0(folder_main, "example/", image_name, "-01-green.tiff"))
writeImage(image_rolled, paste0(folder_main, "example/", image_name, "-02-rolled.tiff"))
writeImage(image_thresholded, paste0(folder_main, "example/", image_name, "-03-threshold.tiff"))
#writeImage(colorLabels(image_object), paste0(folder_main, "example/", image_name, "-03a-object.tiff"))
writeImage(image_round, paste0(folder_main, "example/", image_name, "-04-round_object.tiff"))
writeImage(normalize(image_distancemap), paste0(folder_main, "example/", image_name, "-05-distance_map.tiff"))
writeImage(colorLabels(image_watershed), paste0(folder_main, "example/", image_name, "-06-watershed.tiff"))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiNrbml0cjo6b3B0c19jaHVuaygpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KEVCSW1hZ2UpCmxpYnJhcnkocmV0aWN1bGF0ZSkKZm9sZGVyX21haW4gPC0gIi9Vc2Vycy9jaGFuZy15dS9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3Jhdy9wbGF0ZV9zY2FuL2VtZXJnZW50X2NvZXhpc3RlbmNlX3BsYXRlX3NjYW5fY2hlY2svIgpgYGAKCgpFeGFtcGxlIHBhaXIgCgpEX1Q4X0MxUjJfMQpEX1Q4X0MxUjJfMiAKRF9UOF9DMVIyXzUtOTVfMV8yCgoKYGBge3J9Cmxpc3RfaW1hZ2VzIDwtIHRpYmJsZSgKICAgIGltYWdlX25hbWUgPSBjKCJEX1Q4X0MxUjJfMSIsICJEX1Q4X0MxUjJfMiIsICJEX1Q4X0MxUjJfNS05NV8xXzIiKSwKICAgIGZvbGRlcl9vcmlnaW5hbCA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJjaGVjay9ELTAwLW9yaWdpbmFsLyIpLCAzKSwKICAgIGZvbGRlcl9ncmVlbiA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIpLCAzKSwKICAgIGZvbGRlcl9yb2xsZWQgPSByZXAocGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iKSwgMyksCiAgICBmb2xkZXJfd2F0ZXJzaGVkID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAogICAgZm9sZGVyX3JlZCA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIpLCAzKSwKICAgIGZvbGRlcl9ibHVlID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpCikKCmkgPSAzCmltYWdlX25hbWUgPC0gbGlzdF9pbWFnZXMkaW1hZ2VfbmFtZVtpXQpmb2xkZXJfb3JpZ2luYWwgPC0gbGlzdF9pbWFnZXMkZm9sZGVyX29yaWdpbmFsW2ldCmZvbGRlcl9ncmVlbiA8LSBsaXN0X2ltYWdlcyRmb2xkZXJfZ3JlZW5baV0KZm9sZGVyX3JlZCA8LSBsaXN0X2ltYWdlcyRmb2xkZXJfcmVkW2ldCmZvbGRlcl9ibHVlIDwtIGxpc3RfaW1hZ2VzJGZvbGRlcl9ibHVlW2ldCmZvbGRlcl9yb2xsZWQgPC0gbGlzdF9pbWFnZXMkZm9sZGVyX3JvbGxlZFtpXQpmb2xkZXJfd2F0ZXJzaGVkIDwtIGxpc3RfaW1hZ2VzJGZvbGRlcl9yb2xsZWRbaV0KYGBgCgojIDAuIG9yaWdpbmFsIGltYWdlCgpgYGB7cn0KaW1hZ2Vfb3JpZ2luYWwgPC0gcmVhZEltYWdlKHBhc3RlMChmb2xkZXJfb3JpZ2luYWwsIGltYWdlX25hbWUsICIudGlmZiIpKQp3cml0ZUltYWdlKGltYWdlX29yaWdpbmFsLCBwYXN0ZTAoZm9sZGVyX2dyZWVuLCBpbWFnZV9uYW1lLCAiLTAwLW9yaWdpbmFsLnRpZmYiKSkKZGlzcGxheShpbWFnZV9vcmlnaW5hbCwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKIyAxLiBHcmVlbiBjaGFubmVsCgpgYGB7cn0KdGVtcCA8LSBpbWFnZV9vcmlnaW5hbApjb2xvck1vZGUodGVtcCkgPSBHcmF5c2NhbGUKaW1hZ2VfZ3JlZW4gPC0gdGVtcFssLDJdCndyaXRlSW1hZ2UoaW1hZ2VfZ3JlZW4sIHBhc3RlMChmb2xkZXJfZ3JlZW4sIGltYWdlX25hbWUsICItMDEtZ3JlZW4udGlmZiIpKQpkaXNwbGF5KGltYWdlX2dyZWVuLCBtZXRob2QgPSAicmFzdGVyIikKYGBgClJlZCBjaGFubmVsCgpgYGB7cn0KdGVtcCA8LSBpbWFnZV9vcmlnaW5hbApjb2xvck1vZGUodGVtcCkgPSBHcmF5c2NhbGUKaW1hZ2VfcmVkIDwtIHRlbXBbLCwxXQp3cml0ZUltYWdlKGltYWdlX3JlZCwgcGFzdGUwKGZvbGRlcl9yZWQsIGltYWdlX25hbWUsICItMTEtcmVkLnRpZmYiKSkKZGlzcGxheShpbWFnZV9yZWQsIG1ldGhvZCA9ICJyYXN0ZXIiKQpgYGAKCkJsdWUgY2hhbm5lbAoKYGBge3J9CnRlbXAgPC0gaW1hZ2Vfb3JpZ2luYWwKY29sb3JNb2RlKHRlbXApID0gR3JheXNjYWxlCmltYWdlX2JsdWUgPC0gdGVtcFssLDFdCndyaXRlSW1hZ2UoaW1hZ2VfYmx1ZSwgcGFzdGUwKGZvbGRlcl9ibHVlLCBpbWFnZV9uYW1lLCAiLTIxLWJsdWUudGlmZiIpKQpkaXNwbGF5KGltYWdlX2JsdWUsIG1ldGhvZCA9ICJyYXN0ZXIiKQpgYGAKCgoKCiMgMi4gUm9sbGluZyBiYWxsCgpQeXRob24gY29kZSBmcm9tIGByb2xsaW5nX2JhbGwucHlgCgpgYGB7cn0KcHkkZmlsZV9ncmVlbiA8LSBwYXN0ZTAoZm9sZGVyX2dyZWVuLCBpbWFnZV9uYW1lLCAiLnRpZmYiKQpweSRmaWxlX3JvbGxlZCA8LSBwYXN0ZTAoZm9sZGVyX3JvbGxlZCwgaW1hZ2VfbmFtZSwgIi0wMi1yb2xsZWQudGlmZiIpCmBgYAoKVGhlIFIgdmFyaWFibGVzIHNob3VsZCBiZSBwYXNzZWQgdG8gcHl0aG9uIGJ1dCBzb21laG93IGZhaWxlZC4gTm90IHN1cmUgd2hlcmUgdGhlIHByb2JsZW0gaXMgZnJvbSwgcHJvYmFibHkgcmV0aWN1YWx0ZS4gMjAyMjA5MDIgdXNlIHRoZSBleHRlcm5hbGx5IGdlbmVyYXRlZCByb2xsZWQgaW1hZ2VzLgoKYGBge3B5dGhvbn0KaW1wb3J0IGltYWdlaW8KaW1wb3J0IG9zCmltcG9ydCBzeXMKaW1wb3J0IHNraW1hZ2UKZnJvbSBza2ltYWdlIGltcG9ydCBkYXRhLCByZXN0b3JhdGlvbiwgdXRpbCwgaW8sIGNvbG9yCgpkZWYgcm9sbGluZ19iYWxsX2xpZ2h0KGltYWdlKToKICAgICMgMi4gaW52ZXJ0IHRoZSBpbWFnZQogICAgaW1hZ2VfaW52ZXJ0ZWQgPSB1dGlsLmludmVydChpbWFnZSkKICAgIAogICAgIyAzLiByb2xsaW5nIGJhbGwKICAgIGJhY2tncm91bmRfaW52ZXJ0ZWQgPSByZXN0b3JhdGlvbi5yb2xsaW5nX2JhbGwoaW1hZ2VfaW52ZXJ0ZWQsIHJhZGl1cyA9IDgwLCBudW1fdGhyZWFkcyA9IDEwKQogICAgCiAgICAjIDQuIGludmVydCB0aGUgcmVzdWx0CiAgICBpbWFnZV9yb2xsZWRfaW52ZXJ0ZWQgPSBpbWFnZV9pbnZlcnRlZCAtIGJhY2tncm91bmRfaW52ZXJ0ZWQKICAgIGltYWdlX3JvbGxlZCA9IHV0aWwuaW52ZXJ0KGltYWdlX3JvbGxlZF9pbnZlcnRlZCkKICAgIHJldHVybiBpbWFnZV9yb2xsZWQKICAgIAppbWFnZSA9IGlvLmltcmVhZChmaWxlX2dyZWVuKQppbWFnZV9yb2xsZWQgPSByb2xsaW5nX2JhbGxfbGlnaHQoaW1hZ2UpCmlvLmltc2F2ZShmaWxlX3JvbGxlZCwgaW1hZ2Vfcm9sbGVkKQpgYGAKCkRpc3BsYXkgdGhlIHJvbGxlZCByZXN1bHQKCmBgYHtyfQppbWFnZV9yb2xsZWQgPC0gcmVhZEltYWdlKHBhc3RlMChmb2xkZXJfcm9sbGVkLCBpbWFnZV9uYW1lLCAiLTAyLXJvbGxlZC50aWZmIikpCmRpc3BsYXkoaW1hZ2Vfcm9sbGVkLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKCiMgMy4gVGhyZXNob2xkaW5nCgpIZXJlIGJlY2F1c2UgdGhlIGltYWdlcyBoYXZlIHVuZGVyZ29uZSBncmF5IHNjYWxlIGFuZCBiYWNrZ3JvdW5kIHN1YnRyYWN0aW9uIHNvIEkgYXBwbHkgYSBnbG9iYWwgdGhyZXNob2xkIHRvIHRoZSBpbWFnZQoKYGBge3J9CnRocmVzaG9sZCA8LSBvdHN1KGltYWdlX3JvbGxlZCkKaW1hZ2VfdGhyZXNob2xkZWQgPC0gaW1hZ2Vfcm9sbGVkIDwgdGhyZXNob2xkCmRpc3BsYXkoaW1hZ2VfdGhyZXNob2xkZWQsIG1ldGhvZCA9ICJyYXN0ZXIiKQpgYGAKCiMgNC4gRGV0ZWN0IHJvdW5kIHNoYXBlZCBvYmplY3QgYW5kIHJlbW92ZSBzdXBlciBzbWFsbCBzaXplCgpUbyBzZWxlY3QgcG90ZW50aWFsIGNvbG9uaWVzOiBhcmVhLCBwZXJpbWV0ZXIsIGFuZCBjaXJjdWxhcml0eS4KClRoaXMgc3RlcCBpcyBpbXBsZW1lbnRlZCBoZXJlIHRvIHByZXZlbnQgb3ZlciBzZWdtZW50YXRpb24gYW5kIHJlZHVjZSBzZWdtZW50YXRpb24gbG9hZAoKQXMgeW91IGNhbiBzZWUgaGVyZSwgYXR0YWNoZWQgb2JqZWN0cyBhcmUgbm90IGRpdmlkZWQgeWV0CgpgYGB7cn0KaW1hZ2Vfb2JqZWN0IDwtIGJ3bGFiZWwoaW1hZ2VfdGhyZXNob2xkZWQpCm9iamVjdF9zaGFwZSA8LSBjb21wdXRlRmVhdHVyZXMuc2hhcGUoaW1hZ2Vfb2JqZWN0KSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gIk9iamVjdElEIikKCm9iamVjdF9zaGFwZV9yb3VuZCA8LSBvYmplY3Rfc2hhcGUgJT4lCiAgICAjIEFyZWEKICAgIGZpbHRlcihzLmFyZWEgPiA1MDAgJiBzLmFyZWEgPCA1MDAwMDApICU+JQogICAgIyBSb3VuZG5lc3MgPSAxIG1lYW5zIGEgcGVyZmVjdCBjaXJjbGUKICAgIG11dGF0ZShSb3VuZG5lc3MgPSAocy5yYWRpdXMubWF4IC0gcy5yYWRpdXMubWluKS8yKSAlPiUKICAgIGZpbHRlcihSb3VuZG5lc3MgPiAwLjEgJiBSb3VuZG5lc3MgPCA1MCkgJT4lCiAgICAjIENpcmN1bGFyaXR5ID0gMSBtZWFucyBhIHBlcmZlY3QgY2lyY2xlIGFuZCBnb2VzIGRvd24gdG8gMCBmb3Igbm9uLWNpY3VsYXIgc2hhcGVzCiAgICBtdXRhdGUoQ2lyY3VsYXJpdHkgPSA0ICogcGkgKiBzLmFyZWEgLyBzLnBlcmltZXRlcl4yKSAlPiUKICAgIGZpbHRlcihDaXJjdWxhcml0eSA+IDAuMykgJT4lCiAgICBhcnJhbmdlKGRlc2Mocy5hcmVhKSkKb2JqZWN0X0lEX25vbnJvdW5kIDwtIHdoaWNoKCEob2JqZWN0X3NoYXBlJE9iamVjdElEICVpbiUgb2JqZWN0X3NoYXBlX3JvdW5kJE9iamVjdElEKSkKCmltYWdlX3JvdW5kIDwtIHJtT2JqZWN0cyhpbWFnZV9vYmplY3QsIG9iamVjdF9JRF9ub25yb3VuZCwgcmVlbnVtZXJhdGUgPSBGKQpkaXNwbGF5KGNvbG9yTGFiZWxzKGltYWdlX3JvdW5kKSwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKIyA1LiBEaXN0YW5jZSBtYXAKClRoZSBkaXN0YW5jZSBtYXAgY29udGFpbnMgZm9yIGVhY2ggcGl4ZWwgdGhlIGRpc3RhbmNlIHRvIHRoZSBuZWFyZXN0IGJhY2tncm91bmQgcGl4ZQoKCmBgYHtyfQppbWFnZV9kaXN0YW5jZW1hcCA8LSBkaXN0bWFwKGltYWdlX3JvdW5kKQpkaXNwbGF5KG5vcm1hbGl6ZShpbWFnZV9kaXN0YW5jZW1hcCksIG1ldGhvZCA9ICJyYXN0ZXIiKQpgYGAKCiMgNi4gV2F0ZXJzaGVkCgpUaGlzIGlzIHRoZSBhY3R1YWwgc2VnbWVudGF0aW9uIHN0ZXAuIEhlcmUgdGhlIGFkamFjZW50IG9iamVjdHMgYXJlIGJldHRlciBkaXN0aW5ndWlzaGVkCgpgYGB7cn0KaW1hZ2Vfd2F0ZXJzaGVkIDwtIHdhdGVyc2hlZChpbWFnZV9kaXN0YW5jZW1hcCwgdG9sZXJhbmNlID0gMSkKZGlzcGxheShjb2xvckxhYmVscyhpbWFnZV93YXRlcnNoZWQpLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKCiMgT3V0cHV0IHRoZSBleGFtcGxlCgpgYGB7cn0KIyB3cml0ZUltYWdlKGltYWdlX29yaWdpbmFsLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDAtb3JpZ2luYWwudGlmZiIpKQojIHdyaXRlSW1hZ2UoaW1hZ2VfZ3JlZW4sIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMS1ncmVlbi50aWZmIikpCndyaXRlSW1hZ2UoaW1hZ2Vfcm9sbGVkLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDItcm9sbGVkLnRpZmYiKSkKd3JpdGVJbWFnZShpbWFnZV90aHJlc2hvbGRlZCwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTAzLXRocmVzaG9sZC50aWZmIikpCiN3cml0ZUltYWdlKGNvbG9yTGFiZWxzKGltYWdlX29iamVjdCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wM2Etb2JqZWN0LnRpZmYiKSkKd3JpdGVJbWFnZShpbWFnZV9yb3VuZCwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTA0LXJvdW5kX29iamVjdC50aWZmIikpCndyaXRlSW1hZ2Uobm9ybWFsaXplKGltYWdlX2Rpc3RhbmNlbWFwKSwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTA1LWRpc3RhbmNlX21hcC50aWZmIikpCndyaXRlSW1hZ2UoY29sb3JMYWJlbHMoaW1hZ2Vfd2F0ZXJzaGVkKSwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTA2LXdhdGVyc2hlZC50aWZmIikpCmBgYAoKCgoKCgoKCgoKCgo=